
// BodyView.cpp : implementation of the CBodyView class
//

#include "stdafx.h"

#include "csvline.h"
#include "infdoc.h"
#include "BodyView.h"

#include <utility>
#include "exheader.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//	from afxpriv.h
#define ON_MESSAGE_VOID(message, memberFxn)	{ message, 0, 0, 0, AfxSig_vv, \
		(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))&memberFxn },

typedef std::pair<unsigned, int>	TSTR2WIDTH;
static const int NOT_SORT_BIT = (~(HDF_SORTDOWN | HDF_SORTUP));
static const unsigned NUM_COLUMNS = 7;
static int remapIndex[NUM_COLUMNS] = { 2, 4, 3, 0, 1, 5, 6 };
static LPWSTR gszColumnLabel[NUM_COLUMNS] = 
	{ L"Name", L"Value", L"Code (Type)", L"Root", L"Path", L"[section]", L"tail" };

static const TSTR2WIDTH	colsWidthAlign[NUM_COLUMNS] =
{
	TSTR2WIDTH(100, LVCFMT_LEFT), 
	TSTR2WIDTH(150, LVCFMT_LEFT), 
	TSTR2WIDTH(50, LVCFMT_RIGHT), 
	TSTR2WIDTH(50, LVCFMT_RIGHT), 
	TSTR2WIDTH(150, LVCFMT_LEFT), 
	TSTR2WIDTH(77, LVCFMT_CENTER), 
	TSTR2WIDTH(100, LVCFMT_LEFT)
};

unsigned CBodyView::g_nMode(0);	//	odd is REG
static const int colsAlign[NUM_COLUMNS] = { 
	HDF_LEFT, HDF_LEFT, HDF_RIGHT, HDF_RIGHT, HDF_LEFT, HDF_CENTER, HDF_LEFT 
};

inline void emptyMsgBar(CFrameWnd * pFrameWnd)
{
	try {
		pFrameWnd->GetMessageBar()->SetWindowText(L"");
	}
	catch(...) {}
}

// CBodyView

IMPLEMENT_DYNCREATE(CBodyView, CListView)

BEGIN_MESSAGE_MAP(CBodyView, CListView)
	ON_WM_STYLECHANGED()
	ON_WM_CREATE()
	ON_COMMAND_RANGE(AFX_ID_VIEW_MINIMUM, AFX_ID_VIEW_MAXIMUM, &CBodyView::OnViewStyle)
	ON_COMMAND_RANGE(ID_EDIT_CLEAR_ALL, ID_EDIT_UNDO, CBodyView::OnEditCmds)
	ON_MESSAGE(WM_DDE_TERMINATE, CBodyView::OnNextLineDDE)
	ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI, CBodyView::OnIdleUpdateCmdUI)
	ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_CLEAR_ALL, ID_EDIT_UNDO, CBodyView::OnUpdateEditSel)
	ON_NOTIFY(HDN_ITEMCLICKA, 0, CBodyView::OnHdnItemclick)
	ON_NOTIFY(HDN_ITEMCLICKW, 0, CBodyView::OnHdnItemclick)
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, CBodyView::OnLvnItemchanged)
	ON_WM_CONTEXTMENU()
	ON_WM_SETFOCUS()
END_MESSAGE_MAP()

// CBodyView construction/destruction

CBodyView::CBodyView() : m_nArrow(0)
	{ m_pHeader = new CSortHeaderCtrl; }

CBodyView::~CBodyView()
{
	if (m_pHeader) delete m_pHeader;
}

BOOL CBodyView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CListView::PreCreateWindow(cs);
}

int CBodyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CListView::OnCreate(lpCreateStruct) == -1)
		return -1;

	SetWindowText(L"BODY");

	// TODO:  Add your specialized creation code here
	CListCtrl * pControl = & GetListCtrl();

/* set report style
	long lStyle = ::GetWindowLong(pControl->m_hWnd, GWL_STYLE);
		lStyle &= ~(LVS_TYPEMASK);  // turn off all the style (view mode) bits
		lStyle |= LVS_SMALLICON;	//	LVS_REPORT;       // Set the new style for the control
	::SetWindowLong(pControl->m_hWnd, GWL_STYLE, lStyle);
	*/
	// set other styles
	DWORD dwExStyle = pControl->GetExtendedStyle();	//	LVS_EX_HEADERDRAGDROP | 
		pControl->SetExtendedStyle(dwExStyle | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

	bool bmode = (((g_nMode >> 1) << 1) ^ g_nMode) > 0;
	LV_COLUMN lvc = { LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH };
		lvc.iSubItem = 0;
	int limit = (bmode) ? (-1) : 4;
	for(int i = 0; i < NUM_COLUMNS; lvc.iSubItem = ++i) 
	{
		lvc.pszText = (i > limit) ? gszColumnLabel[i] : L"";
		lvc.cx	= colsWidthAlign[i].first;
		lvc.fmt = (bmode) ? colsWidthAlign[i].second : LVCFMT_LEFT;
		pControl->InsertColumn(i, &lvc);
	}

	m_imageEmpty.Create(1, 1, ILC_MASK, 1, 1);

	m_imageSmall.Create(16, 16, ILC_COLOR8, 1, 1);
	m_imageSmall.Add(AfxGetApp()->LoadIcon(IDR_emptyTYPE));

	m_imageBig.Create(32, 32, ILC_COLOR8, 1, 1);
	m_imageBig.Add(AfxGetApp()->LoadIcon(IDR_emptyTYPE));
	pControl->SetImageList(& m_imageBig, LVSIL_NORMAL);

	return 0;
}

void CBodyView::OnInitialUpdate()
{
	CListView::OnInitialUpdate();
	// TODO: You may populate your ListView with items by directly accessing
	//  its list control through a call to GetListCtrl().

	CListCtrl * pControl = & GetListCtrl();
		pControl->DeleteAllItems();
		emptyMsgBar((CFrameWnd *) AfxGetMainWnd());

//	CString	thefilepath = GetDocument()->GetPathName();
	CString pathfile = GetDocument()->GetPathName();
	if (pathfile.IsEmpty())
	{
		pControl->SetImageList(& m_imageSmall, LVSIL_SMALL);
		pControl->ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);
	}
	else {
		pControl->ModifyStyle(LVS_TYPEMASK, LVS_ICON);
		pControl->SetImageList(& m_imageEmpty, LVSIL_SMALL);
	}
//	int index = testBlank(pControl);
//	if (index + 1) pControl->SetItemText(index, 0, GetDocument()->getDesc());
	LV_ITEM lvi = { LVIF_TEXT | LVIF_PARAM, 0, 0, 0 };
		lvi.pszText = (LPWSTR) cmn::getFileSect()->getDesc();
		lvi.lParam = NULL;

	pControl->InsertItem(&lvi);

	if (m_pHeader->m_hWnd) 
		((CSortHeaderCtrl * )m_pHeader)->OnChildNotify(0, g_nMode, 0, NULL);
	else {
		CHeaderCtrl* pHeader = pControl->GetHeaderCtrl();
		HWND hwndFrom = (pHeader) ? pHeader->GetSafeHwnd() : NULL;
		if (hwndFrom) m_pHeader->SubclassWindow(hwndFrom);
	}
//	if (!pHeader) return;
//	LV_COLUMN lvc = { LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM };
}

// CBodyView diagnostics

#ifdef _DEBUG
void CBodyView::AssertValid() const
{
	CListView::AssertValid();
}

void CBodyView::Dump(CDumpContext& dc) const
{
	CListView::Dump(dc);
}

CinfDoc* CBodyView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CinfDoc)));
	return (CinfDoc*)m_pDocument;
}
#endif //_DEBUG

//	implementation of CBodyView
BOOL CBodyView::toggleMode() 
{
	CListCtrl * pControl = & GetListCtrl();
	BOOL bmode = (++g_nMode & 1);
	//	columns order will be changed
	if (isReportStyle(pControl))
	{
		pControl->DeleteAllItems();
		emptyMsgBar((CFrameWnd *) AfxGetMainWnd());
	}

//	loadHeader(pControl, bmode);
	CHeaderCtrl* pHeader = pControl->GetHeaderCtrl();
//	if (!pHeader) return;
	HWND hwndFrom = (pHeader) ? pHeader->m_hWnd : NULL;
//	LV_COLUMN lvc = { LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM };
	HDITEMW hdi = { HDI_TEXT | HDI_FORMAT, 0, NULL };
	int limit = (bmode) ? (-1) : 4;
	if (hwndFrom)
	for(int i = 0; i < (NUM_COLUMNS - 1); ++i) 
	{
		hdi.pszText = (i > limit) ? gszColumnLabel[i] : L"";
//		hdi.cxy	= colsWidthAlign[i].first;	//	LVCFMT_LEFT;
		hdi.fmt = HDF_STRING | ((bmode) ? colsAlign[i] : HDF_LEFT);	

		if(!::SendMessage(hwndFrom, HDM_SETITEM, i, (LPARAM) (& hdi))) break;
	}

	return bmode;
}

bool CBodyView::doUpdateCmdUI()
{
	CListCtrl * pControl = & (GetListCtrl());
	int	index = pControl->GetItemCount();
	if (index >> 1)
	if (!(isReportStyle(pControl)))
	{
		AfxGetMainWnd()->PostMessageW(WM_COMMAND, ID_VIEW_DETAILS);
		LVFINDINFO findinfo = { LVFI_PARAM, NULL, NULL };
		index = pControl->FindItem(& findinfo);	//	test blank
		if (index + 1) pControl->DeleteItem(index);
		return true;
	}
	return false;
}
void CBodyView::updateMsgBar(CFrameWnd * pFrameWnd)
{
	CListCtrl * pBody = & GetListCtrl();
	UINT nCount = pBody->GetSelectedCount();
	if (nCount && isReportStyle(pBody))
	{
		CString thestr;
		thestr.Format(IDS_SEL_LINES, nCount);
		pFrameWnd->GetMessageBar()->SetWindowText(thestr);
	}
		else emptyMsgBar(pFrameWnd);
}

bool CBodyView::CustomTrackMenu(CListCtrl * pControl)
{
	int nItem(0), thecount = pControl->GetItemCount();
	for ( ; nItem < thecount; ++nItem)
		if (pControl->GetItemState(nItem, LVIS_FOCUSED))
			break;

	CRect therect;
	if (pControl->EnsureVisible(nItem, FALSE))
	if (pControl->GetItemRect(nItem, & therect, LVIR_SELECTBOUNDS))
	{
		POINT thepoint = therect.CenterPoint();
	//thepoint.y += (therect.bottom - therect.top) / 2;
			GetWindowRect(& therect);
		if (::IsRectEmpty(& therect)) return false;

		thepoint.x = (therect.right - therect.left) / 3;
	//thepoint.y += ::GetSystemMetrics(SM_CYICON);
		ClientToScreen(& thepoint);
		CWnd * pMainWnd = AfxGetMainWnd();
		CMenu* pmenuSub = pMainWnd->GetMenu()->GetSubMenu(1);
		pmenuSub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, 
				thepoint.x, thepoint.y, pMainWnd);
		return true;
	}

	return false;
}

void CBodyView::appendLine(CListCtrl * anobj, LPARAM apoint)
{
	LVFINDINFO findinfo = { LVFI_PARAM, NULL, apoint };
	int	index = anobj->FindItem(& findinfo);
	if (index + 1) return; //	already have

	LPCSVLINE pRec = (LPCSVLINE) apoint;	//	| LVIF_IMAGE
	LV_ITEM lvi = { LVIF_TEXT | LVIF_PARAM, 0, 0, 0 };
		lvi.iItem = anobj->GetItemCount(); // add at the end of the control
	//	lvi.iImage = 1;	lvi.iSubItem = 0;
		lvi.lParam = apoint;
	//	lvi.mask = LVIF_TEXT | LVIF_PARAM;
			index = (g_nMode & 1) ? remapIndex[0] : 0;
		lvi.pszText = (LPWSTR) pRec->getSubItem(index);
		//TR) aline->m_sAddr.operator LPCWSTR(); 

	int i(1);
	index = anobj->InsertItem(&lvi);
	if (index + 1)
	if (g_nMode & 1)
		for ( ; i < TCSVLINE::COL_COUNT; ++i)
			anobj->SetItemText(index, i, pRec->m_vec[(remapIndex[i])]);
	else 
		for ( ; i < TCSVLINE::COL_COUNT; ++i)
			anobj->SetItemText(index, i, pRec->m_vec[i]);

	anobj->SetItemText(index, TCSVLINE::COL_COUNT, pRec->m_vec[i]);
		++i;
	anobj->SetItemText(index, i, pRec->m_vec[i]);

	return;
}

bool CBodyView::innerCopy(CListCtrl * pControl)
{
static const unsigned colsec = 5;
	CString strtrim(TCSVLINE::szTrimChars);
		strtrim.AppendChar(',');
	//istCtrl * pControl = & (GetListCtrl());
	CMapStringToString one2more;
	int	colcount = pControl->GetHeaderCtrl()->GetItemCount();
	POSITION thepos = pControl->GetFirstSelectedItemPosition();
	if (colcount > colsec) // more than five
	while (thepos) {
		int iItem = pControl->GetNextSelectedItem(thepos);
		int i(0);
		CString thestr, theline = L"";
		for ( ; i < colsec; ++i)
		{
			thestr = pControl->GetItemText(iItem, i);
				theline.Append(TCSVLINE::decorate(thestr));
				theline.Append(L",\t");
		}

		CString thekey = pControl->GetItemText(iItem, colsec);
			thekey.Trim(TCSVLINE::szTrimChars);
			thestr = pControl->GetItemText(iItem, (colsec + 1));
			theline.Append(thestr);
			theline.TrimRight(strtrim);

		if (theline.GetLength())
		{
			thestr = one2more[thekey];
			if (thestr.IsEmpty()) one2more[thekey] = theline;
			else one2more[thekey] = thestr += L"\r\n" + theline;
		}
	}

	if (one2more.IsEmpty()) return false;

	CString theresult;
	CString thekey, thestr;
	for (thepos = one2more.GetStartPosition(); (thepos);)
	{
		one2more.GetNextAssoc(thepos, thekey, thestr);
		if (thekey.GetLength()) 
			theresult.AppendFormat(L"\r\n[%s]", thekey);
		(theresult += L"\r\n") += thestr;
	}
	theresult.Append(L"\r\n");

	cmn::ClipboardCopyText(theresult);

	return true;
}

// CBodyView message handlers
void CBodyView::OnStyleChanged(int nStyleType, LPSTYLESTRUCT lpStyleStruct)
{
	//TODO: add code to react to the user changing the view style of your window
//	if (nStyleType == LVS_LIST)	//	ID_VIEW_LIST)
	CListView::OnStyleChanged(nStyleType, lpStyleStruct);
}

void CBodyView::OnIdleUpdateCmdUI()
{
	CListCtrl * pControl = & (GetListCtrl());
	int	index = pControl->GetItemCount();
	if (index >> 1)
	if (!(isReportStyle(pControl)))
	{
		PostMessageW(WM_COMMAND, ID_VIEW_DETAILS);
		LVFINDINFO findinfo = { LVFI_PARAM, NULL, NULL };
		index = pControl->FindItem(& findinfo);	//	test blank
		if (index + 1) pControl->DeleteItem(index);
	}
}

LRESULT CBodyView::OnNextLineDDE(WPARAM wParam, LPARAM lParam)
{
	if (wParam) return 0;
	LPCSVLINE pRec = GetDocument()->getLine(lParam);
	if (pRec) if (pRec->getType())
		if (pRec->isReg()) appendLine(& (GetListCtrl()), (LPARAM) pRec);
		else if (((g_nMode >> 1) << 1) == g_nMode)
			appendLine(& (GetListCtrl()), (LPARAM) pRec);
	return 0;
}

void CBodyView::OnUpdateEditSel(CCmdUI *pCmdUI)
{
	if(pCmdUI->m_nID ^ ID_EDIT_SELECT_ALL)
	{
		CListCtrl * pControl = & (GetListCtrl());
		if (pControl->GetFirstSelectedItemPosition())
				pCmdUI->Enable(isReportStyle(pControl));
			else pCmdUI->Enable(FALSE);
	}
	else
		pCmdUI->Enable(GetListCtrl().GetItemCount());
}

void CBodyView::OnHdnItemclick(NMHDR *pNMHDR, LRESULT *pResult)
{
	*pResult = 0;
	if (!pNMHDR) return;	//	WM_NOTIFY
	int  index = ((LPNMHEADER) pNMHDR)->iItem;
//		int  index = ((LPHEADER) m_pHeader)->OnClick(pNMHDR);
	if (index < 0) return;

	++m_nArrow;
	if ((g_nMode & 1) && (index < NUM_COLUMNS))
		 index = remapIndex[index];

	GetListCtrl().SortItems(TCSVLINE::VEC_CMP_PROC[(m_nArrow & 1)], index);

	return;
}

void CBodyView::OnContextMenu(CWnd* pWnd, CPoint apoint)
{
	CListCtrl * pControl = & GetListCtrl();
	// TODO: view style (report : 1, else 0)
	int menuindex = isReportStyle(pControl) ? EDIT_MENU : FILE_MENU;
	if (cmn::CommonTrackMenu(this, apoint, menuindex)) return;

	if (menuindex) // report style
		if (CustomTrackMenu(pControl))
			return;

	AfxGetMainWnd()->PostMessage(WM_CONTEXTMENU, 0, (-1));

	return;
}

void CBodyView::OnViewStyle(UINT nCommandID)
{		
	DWORD dwStyle = -1;
	switch (nCommandID)
	{
	case ID_VIEW_LINEUP:
			// ask the list control to snap to grid
			GetListCtrl().Arrange(LVA_SNAPTOGRID);
		break;

	// other commands change the style on the list control
	case ID_VIEW_DETAILS:
		dwStyle = LVS_REPORT;
		break;

	case ID_VIEW_SMALLICON:
		dwStyle = LVS_SMALLICON;
		break;

	case ID_VIEW_LARGEICON:
		dwStyle = LVS_ICON;
		break;

	default:	// case ID_VIEW_LIST:
		toggleMode();
//		theApp.DeleteContents();
		return;
	}

	// change the style; window will repaint automatically
	if (dwStyle + 1) GetListCtrl().ModifyStyle(LVS_TYPEMASK, dwStyle);
	return;
}

void CBodyView::OnEditCmds(UINT nCommandID)
{
	CListCtrl * pBody = & GetListCtrl();
	if (pBody)
	switch (nCommandID)
	{
	case ID_EDIT_SELECT_ALL:
			OnEditSelectAll(pBody);
		return;

	case ID_EDIT_COPY :
			if (!(innerCopy(pBody)))
			AfxMessageBox(IDS_NO_SEL_ITEMS, MB_OK | MB_ICONWARNING);
		return;
	}
		else return;
//		case ID_EDIT_CUT : 
	innerCopy(pBody);
	POSITION thepos = pBody->GetFirstSelectedItemPosition();
	while (thepos) {
		int iItem = pBody->GetNextSelectedItem(thepos);
		pBody->DeleteItem(iItem);
		thepos = pBody->GetFirstSelectedItemPosition();
	}	//		case ID_EDIT_CUT : 
	return;
}

void CBodyView::OnEditSelectAll(CListCtrl * pBody)
{
	//istCtrl * pBody = & GetListCtrl();
	int i(0), count = pBody->GetItemCount();
	for ( ; i < count; ++i)
	{
		if (pBody->GetItemState(i, LVIS_SELECTED)) continue;

		for (int j(i); j < count; ++j)
			pBody->SetItemState(j, LVIS_SELECTED, LVIS_SELECTED); 
		break;
	}

	if (i == count)
	for (int i(0); i < count; ++i)
		pBody->SetItemState(i, 0, LVIS_SELECTED); 
}

void CBodyView::OnSetFocus(CWnd* pOldWnd)
{
	CWnd::OnSetFocus(pOldWnd);
	// TODO: Add your message handler code here
	try {
		updateMsgBar((CFrameWnd *) AfxGetMainWnd());
	}
	catch(...) {}
}

void CBodyView::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
//	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: Add your control notification handler code here
	try {
		updateMsgBar((CFrameWnd *) AfxGetMainWnd());
	}
	catch(...) {}
}
